﻿using System;
using System.Net;

using Hammock;
using Hammock.Authentication.OAuth;
using Hammock.Web;

namespace Tequila.Phone.Sharing.Twitter
{
    /// <summary>
    /// Twitter sharing service.
    /// </summary>
    public class TwitterShareService : BaseShareService
    {
        private string _oAuthTokenSecret;
        private string _oAuthToken;
        private string _loginUrl;
        private byte _oauthInvalidRequestCount;

        private const string SettingsTokenKey = "twitter_access_token";
        private const string SettingsTokenSecret = "twitter_access_token_secret";
        private const byte OauthRequestLimit = 10;

        private readonly ISettingsService _settingsService;
        private readonly TwitterAuthorizationSession _session;

        /// <summary>
        /// Initializes new instance of class.
        /// </summary>
        /// <param name="settingsService"></param>
        /// <param name="session"></param>
        /// <exception cref="ArgumentNullException"></exception>
        /// <exception cref="ShareServiceException"></exception>
        public TwitterShareService(ISettingsService settingsService, TwitterAuthorizationSession session)
        {
            if (settingsService == null)
            {
                throw new ArgumentNullException("settingsService");
            }
            if (session == null)
            {
                throw new ArgumentNullException("session");
            }
            if (!session.IsValid)
            {
                throw new ShareServiceException(ShareServiceErrorType.InvalidSession);
            }

            _settingsService = settingsService;
            _session = session;
        }

        /// <summary>
        /// Performs initialization.
        /// </summary>
        public override void Init()
        {
            LoadAppToken();
        }


        /// <summary>
        /// Returns OAUTH login <see cref="T:System.Uri">uri</see>.
        /// </summary>
        /// <returns>
        /// Type: <see cref="T:System.Uri"/>.
        /// </returns>
        public override Uri GetOAuthLoginUri()
        {
            return string.IsNullOrEmpty(_loginUrl) ? null : new Uri(_loginUrl);
        }

        /// <summary>
        /// Determines whether authentication performed or not.
        /// </summary>
        /// <returns></returns>
        public override bool IsAuthenticated()
        {
            return _settingsService.Contains(BuildSettingsTokenKey());
        }

        private string BuildSettingsTokenKey()
        {
            var key = string.Format("{0}{1}", SettingsTokenKey, CurrentUserId);
            return key;
        }

        private string BuildSettingsTokenSecret()
        {
            var key = string.Format("{0}{1}", SettingsTokenSecret, CurrentUserId);
            return key;
        }

        /// <summary>
        /// Shares <see cref="GaleryImage">image</see>.
        /// </summary>
        /// <param name="image"></param>
        public override void PostImage(GaleryImage image)
        {
            _oAuthToken = _settingsService.Get(BuildSettingsTokenKey()) as string;
            _oAuthTokenSecret = _settingsService.Get(BuildSettingsTokenSecret()) as string;
            var title = string.Format("{0}. {1}", image.GalleryTitle, image.Title);
            if (!string.IsNullOrEmpty(image.By))
                title += string.Format(" ({0})", image.By);
            PostTweetWithImage(title, image.Link);
        }

        /// <summary>
        /// Shares <see cref="Article">article</see>.
        /// </summary>
        /// <param name="article"></param>
        public override void Post(Article article)
        {
            _oAuthToken = _settingsService.Get(BuildSettingsTokenKey()) as string;
            _oAuthTokenSecret = _settingsService.Get(BuildSettingsTokenSecret()) as string;

            if (article.IsWithThumbnail)
                PostTweetWithImage(BuildMessage(article), article.ThumbnailLink);
            else
                PostTweet(BuildMessage(article));
        }

        /// <summary>
        /// Processes OAUTH output requested by <see cref="T:System.Uri">uri</see>.
        /// </summary>
        public override void ProcessOAuthBrowserResult(Uri openedUri)
        {
            try
            {
                if (!openedUri.AbsoluteUri.Contains(_session.CallbackUrl))
                    return;
            }
            catch
            {
                return;
            }

            var arguments = openedUri.AbsoluteUri.Split('?');
            if (arguments.Length < 2)
                return;

            LoadAccessToken(arguments[1]);

        }

        /// <summary>
        /// Returns logout url.
        /// </summary>
        /// <returns>
        /// Type: <see cref="T:System.Uri"/>.
        /// </returns>
        public override Uri GetLogoutUrl()
        {
            return null;
        }

        /// <summary>
        /// Creates custom Login uri.
        /// </summary>
        public override void BuildLoginUri()
        {
            
        }

        private void LoadAppToken()
        {
            var credentials = new OAuthCredentials
                                  {
                                      Type = OAuthType.RequestToken,
                                      SignatureMethod = OAuthSignatureMethod.HmacSha1,
                                      ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                                      ConsumerKey = _session.ConsumerKey,
                                      ConsumerSecret = _session.ConsumerSecret,
                                      Version = TwitterOAuthUtility.OAuthVersion,
                                      CallbackUrl = _session.CallbackUrl
                                  };

            var client = new RestClient
                             {
                                 Authority = "https://api.twitter.com/oauth",
                                 Credentials = credentials,
                                 HasElevatedPermissions = true
                             };

            var request = new RestRequest
            {
                Path = "/request_token"
            };

            client.BeginRequest(request, TwitterRequestTokenCompleted);
        }

        private void TwitterRequestTokenCompleted(RestRequest request, RestResponse response, object userstate)
        {
            _oAuthToken = GetQueryParameter(response.Content, "oauth_token");
            _oAuthTokenSecret = GetQueryParameter(response.Content, "oauth_token_secret");

            var innerException = response.InnerException;
            if(innerException != null && response.InnerException is WebException &&
                ((WebException)response.InnerException).Status == WebExceptionStatus.UnknownError)
            {
                OnConnectionError((WebException)innerException);
                return;
            }

            if (String.IsNullOrEmpty(_oAuthToken) || String.IsNullOrEmpty(_oAuthTokenSecret))
            {
                if(_oauthInvalidRequestCount > OauthRequestLimit)
                {
                    OnInitError("Can't init twitter authorization");
                    return;
                }

                _oauthInvalidRequestCount++;
                LoadAppToken();
                return;
            }

            _oauthInvalidRequestCount = 0;

            if (IsAuthenticated())
            {
                _loginUrl = TwitterOAuthUtility.AuthorizeUri + "?oauth_token=" + _oAuthToken;
            }
            else
            {
                _loginUrl = TwitterOAuthUtility.AuthorizeUri + "?oauth_token=" + _oAuthToken + "&force_login=true";
            }

            OnInitSuccess();
        }

        private void LoadAccessToken(string uri)
        {
            var requestToken = GetQueryParameter(uri, "oauth_token");
            if (requestToken != _oAuthToken)
            {
                OnLoginError("Twitter auth tokens don't match");
                return;
            }

            var requestVerifier = GetQueryParameter(uri, "oauth_verifier");

            var credentials = new OAuthCredentials
            {
                Type = OAuthType.AccessToken,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = _session.ConsumerKey,
                ConsumerSecret = _session.ConsumerSecret,
                Token = _oAuthToken,
                TokenSecret = _oAuthTokenSecret,
                Verifier = requestVerifier
            };

            var client = new RestClient
            {
                Authority = "https://api.twitter.com/oauth",
                Credentials = credentials,
                HasElevatedPermissions = true
            };

            var request = new RestRequest
            {
                Path = "/access_token"
            };

            client.BeginRequest(request, RequestAccessTokenCompleted);
        }

        private void RequestAccessTokenCompleted(RestRequest request, RestResponse response, object userstate)
        {
            _oAuthToken = GetQueryParameter(response.Content, "oauth_token");
            _oAuthTokenSecret = GetQueryParameter(response.Content, "oauth_token_secret");

            if (String.IsNullOrEmpty(_oAuthToken) || String.IsNullOrEmpty(_oAuthTokenSecret))
            {
                if(IsAuthenticated())
                {
                    _oAuthToken = _settingsService.Get(BuildSettingsTokenKey()) as string;
                    _oAuthTokenSecret = _settingsService.Get(BuildSettingsTokenSecret()) as string;
                    OnLoginSuccess();
                    return;
                }
                OnLoginError("Login failed");
                return;
            }

            _settingsService.Save(BuildSettingsTokenKey(), _oAuthToken);
            _settingsService.Save(BuildSettingsTokenSecret(), _oAuthTokenSecret);

            OnLoginSuccess();
        }

        private void PostTweet(string text)
        {
            var credentials = new OAuthCredentials
            {
                Type = OAuthType.ProtectedResource,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = _session.ConsumerKey,
                ConsumerSecret = _session.ConsumerSecret,
                Token = _oAuthToken,
                TokenSecret = _oAuthTokenSecret,
                Version = TwitterOAuthUtility.OAuthVersion,
            };

            var client = new RestClient
            {
                Authority = "http://api.twitter.com",
                HasElevatedPermissions = true
            };

            var request = new RestRequest
            {
                Credentials = credentials,
                Path = "/statuses/update.xml",
                Method = WebMethod.Post
            };


            request.AddParameter("status", text);
            request.AddParameter("wrap_links", "true");

            client.BeginRequest(request, PostTweetCompleted);

        }

        private void PostTweetWithImage(string text, string localImagePath)
        {
            var credentials = new OAuthCredentials
            {
                Type = OAuthType.ProtectedResource,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
                ConsumerKey = _session.ConsumerKey,
                ConsumerSecret = _session.ConsumerSecret,
                Token = _oAuthToken,
                TokenSecret = _oAuthTokenSecret,
                Version = TwitterOAuthUtility.OAuthVersion,
            };

            var client = new RestClient
            {
                Authority = "https://upload.twitter.com/1",
                HasElevatedPermissions = true
            };


            var requestImage = new RestRequest
            {
                Credentials = credentials,
                Path = "/statuses/update_with_media.xml",
                Method = WebMethod.Post
            };

            requestImage.AddParameter("status", text);
            requestImage.AddParameter("wrap_links", "true");
            requestImage.AddFile("media[]", localImagePath,
                localImagePath, "image/jpeg");

            client.BeginRequest(requestImage, PostTweetCompleted);
        }

        private void PostTweetCompleted(RestRequest request, RestResponse response, object userstate)
        {
            if (response.StatusCode == HttpStatusCode.OK)
                OnPostSuccess();
            else
                OnPostError(null);
        }


        private static string GetQueryParameter(string input, string parameterName)
        {
            foreach (var item in input.Split('&'))
            {
                var parts = item.Split('=');
                if (parts[0] == parameterName)
                    return parts[1];
            }
            return String.Empty;
        }

        private static string BuildMessage(Article article)
        {
            return string.Format("{1} - {0}", article.OriginalLink, article.Title);
        }

    }
}
